/**
 *
 * @Author: Marek Zakowicz <mazak (at) debian (dot) linux.org.pl>
 *
 * Copyright (c) 2005, 2006, 2007 Marek Zakowicz. All rights reserved.
 *
 * Licence: This code may be used, (re)distribute and/or modify under the terms one of the following two kinds of licence:
 *          1) as free software under GPL 3 (General Public Licence ver. 3 as published by the Free Software Foundation)
 *          2) as software under the terms individual licence from Marek Zakowicz
 *
 *          This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *          without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

package jsp2php;

import java.util.*;

/**
 * @has 1 value_type_map 9..* JavaType
 * @has 1 type_map       9..* JavaType
 */
class JavaType {
   private String      name;     // int, java.lang.String
   private boolean     is_value; // false for eg.: class java.lang.String, //, statement
                                 // true for eg.: java.lang.String, int
   private ITranslator translator;

   private static JavaType _boolean;
   private static JavaType _double;
   private static JavaType _int;
   private static JavaType _string;

   private static JavaType comment   = new JavaType("# comment", false, null);
   private static JavaType statement = new JavaType("# statement", false, null);
   private static JavaType voidType  = new JavaType("# void", false, null);

   private static LinkedHashMap<String, JavaType> value_type_map = new LinkedHashMap<String, JavaType>(); // is_value = true
   private static LinkedHashMap<String, JavaType> type_map = new LinkedHashMap<String, JavaType>(); // is_value = false


   static {

      try {
         Class.forName("jsp2php.BoolTranslator");
         Class.forName("jsp2php.DoubleTranslator");
         Class.forName("jsp2php.IntTranslator");
         Class.forName("jsp2php.StringTranslator");
      }
      catch(Exception e) {
         System.err.println("Error during initialization of JavaType: "+e);
      }

      _boolean = value_type_map.get("boolean");
      _double  = value_type_map.get("double");
      _int     = value_type_map.get("int");
      _string  = value_type_map.get("String");

      try {
         Class.forName("jsp2php.BooleanTranslator");
         Class.forName("jsp2php.DoubleClassTranslator");
         Class.forName("jsp2php.IntegerTranslator");
         Class.forName("jsp2php.HttpServletRequestTranslator");
         Class.forName("jsp2php.HttpServletResponseTranslator");
      }
      catch(Exception e) {
         System.err.println("Error during initialization of JavaType: "+e);
      }

      // TODO: Powinno byc jeszcze podczytywanie klas w oparciu o konfig

      type_map.put("# comment", comment);
      type_map.put("# statement", statement);

   }


   // Jesli ukryje konstruktor, i przejde na wzorzec singletonowy, ma sens porownywanie operatorem ==
   private JavaType(String name, boolean is_value, ITranslator translator) {
      this.name       = name;
      this.is_value   = is_value;
      this.translator = translator;
   }

   String name() {
      return name;
   }

   boolean is_value() {
      return is_value;
   }

   ITranslator translator() {
      return translator;
   }

   static JavaType bool() {
      return _boolean;
   }

   static JavaType _double() {
      return _double;
   }

   static JavaType integer() {
      return _int;
      //return value_type_map.get("int");
   }

   static JavaType string() {
      return _string;
      //return value_type_map.get("java.lang.String");
   }

   static JavaType comment() {
      return comment;
   }

   static JavaType statement() {
      return comment;
   }

   static JavaType voidType() {
      return voidType;
   }

   static JavaType add_type_translator( ITypeTranslator tr ) {
      // TODO : tr != null
      String type_id = tr.name(); // TODO: type_id != null

      // type_map update
      JavaType jt = type_map.get(type_id), vjt = null;
      if( jt != null ) {
         throw new Error("Type translator registration error: id '"+ type_id +"' point to '"+ jt.translator().getClass().getName() +"' object!");
      }
      jt = new JavaType(type_id, false, tr);
      type_map.put(type_id, jt);

      // value_type_map update
      IValueTranslator vtr = tr.value_translator();
      if( vtr != null ) {
         vjt = new JavaType(type_id, true, vtr);
         value_type_map.put(type_id, vjt);
      }

      // packages update
      String types[]   = type_id.split( "\\." );
      int    last_type = types.length - 1;

      if( last_type <= 0 ) { // when last_type < 0 then we have error...
         return vjt != null ? vjt : jt;
      }

      PackageTranslator pkg_tr          = null;
      StringBuilder     type_id_builder = new StringBuilder();
      String            pkg_type_id     = null;

      for(int i=0; i < last_type; i++) {
         String   cur_type_id = type_id_builder.append( types[ i ] ).toString();
         JavaType cur_type    = type_map.get( cur_type_id );

         if( cur_type == null ) {
            PackageTranslator cur_pkg_tr = new PackageTranslator( cur_type_id );
            cur_type = new JavaType(cur_type_id, false, cur_pkg_tr);
            type_map.put( cur_type_id, cur_type ); // type_map update with new package translator
            if( pkg_tr != null ) {
               pkg_tr.add_field( types[i], cur_type );
            }
         }
         pkg_tr = (PackageTranslator) cur_type.translator();
         pkg_type_id = cur_type_id;
         type_id_builder.append(".");
      }

      String last_type_id = types[ last_type ];

      if( pkg_type_id.equals( "java.lang" ) ) {
         type_map.put(last_type_id, jt);
         if( vjt != null ) {
            value_type_map.put(last_type_id, vjt);
         }
      }
      pkg_tr.add_field( last_type_id , jt );

      return vjt != null ? vjt : jt;
   }

   //TODO: static void init_name_space(NameSpace ns, String package);
   static void init_name_space(NameSpace ns) {
      for( String type_id: type_map.keySet() ) {
         if( type_id.startsWith("#") ) {
            continue;
         }
         //System.err.println("declare "+ type_id +" -> "+ type_map.get(type_id).translator().getClass().getName());
         //ns.declare(type_id, (ITypeTranslator) type_map.get(type_id).translator());
         ns.declare(type_id, type_map.get(type_id));
      }
   }

   static JavaType value_type(String type_id) throws TranslatorException {
      // W przyszlosci interesujaca moze byc opcja rejestrowania typow przez translatory - moze uda sie to zrealizaowac na przykladzie klasy Math, jesli starczy czasu...
      JavaType jt = value_type_map.get(type_id);
      if( jt == null ) {
         throw new TranslatorException("Value type "+ type_id +" is not available (feel free to write a translator).");
      }
      return jt;
   }

   static IValueTranslator value_translator(String type_id) throws TranslatorException {
      return (IValueTranslator) value_type(type_id).translator();
   }

   static JavaType non_value_type(String id) throws TranslatorException {
      JavaType jt = type_map.get(id);
      if( jt == null ) {
         throw new TranslatorException("Non value type "+ id +" is not available (feel free to write a translator).");
      }
      return jt;
   }

   public String toString() {
      return name;
   }
}
